#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define  DEVICE_NAME "first-driver"
#define  CLASS_NAME  "first"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hanseltu");
MODULE_DESCRIPTION("A simple Linux char driver for the haoxin-ubuntu");
MODULE_VERSION("0.1");

static int    majorNumber; //store the device number ---auto
static char   message[256] = {0};
static short  size_of_message;
static int    numberOpens = 0;
static struct class*  firstdriverClass  = NULL; //class struct pointer
static struct device* firstdriverDevice = NULL; //device struct pointer

//the prototype functions for the character driver
static int     dev_open(struct inode *, struct file *);
static int     dev_release(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t, loff_t *);

//main struct,used for the above four function
static struct file_operations fops =
{
   .open = dev_open,
   .read = dev_read,
   .write = dev_write,
   .release = dev_release,
};

//init the driver
static int __init firstdriver_init(void){
   printk(KERN_INFO "First-Driver: Initializing the First-Driver LKM\n");


   majorNumber = register_chrdev(0, DEVICE_NAME, &fops); //dymatic produced
   if (majorNumber<0){
      printk(KERN_ALERT "First-Driver failed to register a major number\n");
      return majorNumber;
   }
   printk(KERN_INFO "First-Driver: registered correctly with major number %d\n", majorNumber);

    //register the device class
   firstdriverClass = class_create(THIS_MODULE, CLASS_NAME);
   if (IS_ERR(firstdriverClass)){
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to register device class\n");
      return PTR_ERR(firstdriverClass);
   }
   printk(KERN_INFO "First-Driver: device class registered correctly\n");

    //register the device driver
   firstdriverDevice = device_create(firstdriverClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
   if (IS_ERR(firstdriverDevice)){
      class_destroy(firstdriverClass);
      unregister_chrdev(majorNumber, DEVICE_NAME);
      printk(KERN_ALERT "Failed to create the device\n");
      return PTR_ERR(firstdriverDevice);
   }
   printk(KERN_INFO "First-Driver: device class created correctly\n");
   return 0;
}

//exit the driver
static void __exit firstdriver_exit(void){
   device_destroy(firstdriverClass, MKDEV(majorNumber, 0));
   class_unregister(firstdriverClass);
   class_destroy(firstdriverClass);
   unregister_chrdev(majorNumber, DEVICE_NAME);
   printk(KERN_INFO "First-Driver: Goodbye from the LKM!\n");
}


static int dev_open(struct inode *inodep, struct file *filep){
   numberOpens++;
   printk(KERN_INFO "First-Driver: Device has been opened %d time(s)\n", numberOpens);
   return 0;
}


static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
   int error_count = 0;

   error_count = copy_to_user(buffer, message, size_of_message);

   if (error_count==0){
      printk(KERN_INFO "First-Driver: Sent %d characters to the user\n", size_of_message);
      return (size_of_message=0);
   }
   else {
      printk(KERN_INFO "First-Driver: Failed to send %d characters to the user\n", error_count);
      return -EFAULT;
   }
}


static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
   sprintf(message, "%s(%zu letters)", buffer, len);
   size_of_message = strlen(message);
   printk(KERN_INFO "First-Driver: Received %zu characters from the user\n", len);
   return len;
}


static int dev_release(struct inode *inodep, struct file *filep){
   printk(KERN_INFO "First-Driver: Device successfully closed\n");
   return 0;
}


module_init(firstdriver_init);
module_exit(firstdriver_exit);
